﻿//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	WebFormPresenter.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web.UI;
using System.Web.UI.WebControls;
using FluentRibbon;
using FluentRibbon.Definitions;
using FluentRibbon.Definitions.Controls;
using FluentRibbon.Libraries;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Smart.Common.Rules;
using Smart.Common.WebControls;

namespace Smart.Common.Forms
{
	/// <summary>
	/// SharePoint form presenter
	/// </summary>
	[Localizable(false)]
	public class WebFormPresenter
	{
		/// <summary>
		/// List metadata
		/// </summary>
		public MetaList Meta { get; set; }

		/// <summary>
		/// Form mode
		/// </summary>
		public MetaFormMode Mode { get; set; }

		/// <summary>
		/// User
		/// </summary>
		public SPUser User { get; set; }

		/// <summary>
		/// Rules service
		/// </summary>
		public IRulesService RulesService { get; set; }

		/// <summary>
		/// Rules instance
		/// </summary>
		public IRulesInstance RulesInstance { get; set; }

		/// <summary>
		/// Rules values
		/// </summary>
		public Dictionary<string, object> RulesValues { get; set; }

		/// <summary>
		/// Tokens, used in substituion replacement (like in '##SiteUrl##/_settings.aspx')
		/// </summary>
		public Dictionary<string, object> Tokens { get; set; }

		private Page _page;

		/// <summary>
		/// Current page
		/// </summary>
		public Page Page
		{
			get { return _page ?? (_page = WebExts.GetPage()); }
			set { _page = value; }
		}

		/// <summary>
		/// Unknown meta add
		/// </summary>
		public event Func<ControlCollection, MetaFormCtrl, bool> AddCtrlEv;

		/// <summary>
		/// SharePoint form presenter
		/// </summary>
		protected WebFormPresenter()
		{
			User = SPH.GetCurrentUser();
		}

		/// <summary>
		/// SharePoint form presenter
		/// </summary>
		public WebFormPresenter(MetaList meta, MetaFormMode mode)
			: this()
		{
			Meta = meta;
			Mode = mode;
		}

		/// <summary>
		/// SharePoint form presenter
		/// </summary>
		public WebFormPresenter(MetaFormMode mode)
			: this()
		{
			Mode = mode;
		}

		/// <summary>
		/// Return current language id
		/// </summary>
		public virtual int GetCurrentLCID()
		{
			return SPH.GetCurrentLCID();
		}

		/// <summary>
		/// Create, init and add form control to container
		/// </summary>
		public virtual void AddFormControls(ControlCollection container, MetaForm form)
		{
			form.Control = container;

			var items = form.Items;

			for (int I = 0, ICnt = items.Count; I < ICnt; I++)
			{
				var ctrl = items[I];

				if (ctrl is MetaFormTabs)
				{
					AddTabs(container, (MetaFormTabs)ctrl);
				}
				else if (ctrl is MetaFormGroup)
				{
					AddGroup(container, (MetaFormGroup)ctrl);
				}
				else if (ctrl is MetaFormTable)
				{
					AddTable(container, (MetaFormTable)ctrl);
				}
				else if (ctrl is MetaFormControl)
				{
					if (((MetaFormControl)ctrl).IsField)
					{
						var fields = new List<MetaFormCtrl>();

						I = DetectFieldGroup(items, I, fields);

						AddFieldGroup(container, fields);
					}
					else
					{
						AddControl(container, (MetaFormControl)ctrl);
					}
				}
				else if (ctrl is MetaFormFieldCtrl)
				{
					var fields = new List<MetaFormCtrl>();

					I = DetectFieldGroup(items, I, fields);

					AddFieldGroup(container, fields);
				}
				else if (ctrl is MetaFormScripts)
				{
					AddScripts(container, (MetaFormScripts)ctrl);
				}
				else if (!FireAddCtrlEv(container, ctrl))
				{
					throw new NotSupportedException("AddFormControls {" + ctrl.GetType().FullName + "}");
				}
			}

			AddButtonTabs(form.ButtonTabs);

			Page.PreRender += OnPagePreRender;
		}

		/// <summary>
		/// Create, init and add BUTTON TABS to Page
		/// </summary>
		protected virtual void AddButtonTabs(MetaFormButtonTabs btabs)
		{
			if (btabs == null)
				return;

			var tabs = btabs.Items;

			tabs.Sort((tab1, tab2) => string.Compare(tab1.Title, tab2.Title, StringComparison.OrdinalIgnoreCase));

			var initialTabIdx = -1;
			MetaFormButtonTab initialTab = null;

			for (int I = 0, ICnt = tabs.Count; I < ICnt; I++)
			{
				var btab = tabs[I];

				if (btab.Initial && initialTab == null)
				{
					initialTab = btab;
					initialTabIdx = I;
				}
				else
				{
					btab.Initial = false;
				}
			}

			for (int I = 0, ICnt = tabs.Count; I < ICnt; I++)
			{
				if (I == initialTabIdx)
					continue;

				var btab = tabs[I];

				AddButtonTab(btab);
			}

			if (initialTab != null)
				AddButtonTab(initialTab);
		}

		/// <summary>
		/// Create, init and add BUTTON TAB to Page
		/// </summary>
		public virtual void AddButtonTab(MetaFormButtonTab buttonTab)
		{
			if (!CheckSecurity(buttonTab.Security) || !buttonTab.Visible)
				return;

			if (!buttonTab.Condition.IsNullOrEmpty() && RulesService != null && !RulesService.Check((object)buttonTab.Condition, RulesInstance, RulesValues))
				return;

			var td = new TabDefinition
						{
							Id = buttonTab.Name,
							Title = buttonTab.Title
						};

			var groupDefs = new List<GroupDefinition>();

			buttonTab.Control = td;

			var groups = buttonTab.Items;

			for (int I = 0, ICnt = groups.Count; I < ICnt; I++)
			{
				var group = groups[I];

				var gd = GetButtonGroupDefinition(group);

				if (gd != null)
					groupDefs.Add(gd);
			}

			if (groupDefs.Count == 0)
				return;

			td.Groups = groupDefs.ToArray();

			RibbonController.Current.AddRibbonTabToPage(td, Page, buttonTab.Initial);
		}

		/// <summary>
		/// Create BUTTON GROUP definition
		/// </summary>
		public virtual GroupDefinition GetButtonGroupDefinition(MetaFormButtonGroup buttonGroup)
		{
			if (!CheckSecurity(buttonGroup.Security) || !buttonGroup.Visible)
				return null;

			if (!buttonGroup.Condition.IsNullOrEmpty() && RulesService != null && !RulesService.Check((object)buttonGroup.Condition, RulesInstance, RulesValues))
				return null;

			var gd = new GroupDefinition
						{
							Id = buttonGroup.Name,
							Title = buttonGroup.Title,
							Controls = new ControlDefinition[0]
						};

			switch (buttonGroup.Template)
			{
				case MetaButtonGroupTemplate.SimpleTemplate:
					gd.Template = GroupTemplateLibrary.SimpleTemplate;
					break;

				case MetaButtonGroupTemplate.ThreeRowTemplate:
					gd.Template = GroupTemplateLibrary.ThreeRowTemplate;
					break;

				default:
					throw new ArgumentOutOfRangeException("buttonGroup.Template");
			}

			var controls = new List<ControlDefinition>();

			var buttons = buttonGroup.Items;

			for (int I = 0, ICnt = buttons.Count; I < ICnt; I++)
			{
				var button = buttons[I];

				var bd = GetButtonDefinition(button);

				if (bd != null)
					controls.Add(bd);
			}

			if (controls.Count == 0)
				return null;

			gd.Controls = controls.ToArray();

			return gd;
		}

		/// <summary>
		/// Create BUTTON definition
		/// </summary>
		public virtual ButtonDefinition GetButtonDefinition(MetaFormButton button)
		{
			if (!CheckSecurity(button.Security) || !button.Visible)
				return null;

			if (!button.Condition.IsNullOrEmpty() && RulesService != null && !RulesService.Check((object)button.Condition, RulesInstance, RulesValues))
				return null;

			var bd = new ButtonDefinition
						{
							Id = button.Name,
							Title = button.Title,
							CommandEnableJavaScript = button.CommandEnable.ReplaceTags(Tokens, Const.Template.TagSymbol) ?? (button.Enabled ? "true" : "false"),
							CommandJavaScript = button.Command.ReplaceTags(Tokens, Const.Template.TagSymbol)
						};

			if (button.UseStandardImage)
			{
				bd.Image = ImageLibrary.GetStandardImage(button.ImageX, button.ImageY, button.ImageLang <= 0 ? GetCurrentLCID() : button.ImageLang);
			}
			else
			{
				bd.Image = new ImageDefinition
							{
								Url16 = button.ImageUrl16,
								Url32 = button.ImageUrl32,
								X = button.ImageX,
								Y = button.ImageY
							};
			}

			return bd;
		}

		/// <summary>
		/// Create, init and add TABS control to container
		/// </summary>
		public virtual void AddTabs(ControlCollection container, MetaFormTabs tabs)
		{
			var tabsItems =
				tabs.Items
					.Where(tab =>
						   tab.Condition.IsNullOrEmpty() ||
						   RulesService == null ||
						   RulesService.Check((object)tab.Condition, RulesInstance, RulesValues))
					.ToList();

			var tc =
				new TabContainer
					{
						ID = tabs.Name,
						Width = tabs.Width,
						Visible = tabs.Visible,
						Enabled = tabs.Enabled && !tabs.ReadOnly
					};

			tabs.Control = tc;

			container.Add(tc);

			foreach (var tabsItem in tabsItems)
			{
				var tab =
					new TabPage
						{
							ID = tabsItem.Name,
							Width = tabs.Width,
							Height = tabsItem.Height.IsEmpty ? tabs.Height : tabsItem.Height,
							Visible = tabsItem.Visible,
							Enabled = tabsItem.Enabled && !tabsItem.ReadOnly
						};

				tabsItem.Control = tab;

				tc.Tabs.Add(tab);

				var items = tabsItem.Items;

				for (int I = 0, ICnt = items.Count; I < ICnt; I++)
				{
					var ctrl = items[I];

					if (ctrl is MetaFormGroup)
					{
						AddGroup(tab.Controls, (MetaFormGroup)ctrl);
					}
					else if (ctrl is MetaFormTable)
					{
						AddTable(tab.Controls, (MetaFormTable)ctrl);
					}
					else if (ctrl is MetaFormFieldCtrl)
					{
						var fields = new List<MetaFormCtrl>();

						I = DetectFieldGroup(items, I, fields);

						AddFieldGroup(tab.Controls, fields);
					}
					else if (ctrl is MetaFormControl)
					{
						if (((MetaFormControl)ctrl).IsField)
						{
							var fields = new List<MetaFormCtrl>();

							I = DetectFieldGroup(items, I, fields);

							AddFieldGroup(tab.Controls, fields);
						}
						else
						{
							AddControl(tab.Controls, (MetaFormControl)ctrl);
						}
					}
					else if (ctrl is MetaFormLabelCtrl)
					{
						AddLabel(tab.Controls, (MetaFormLabelCtrl)ctrl);
					}
					else if (!FireAddCtrlEv(tab.Controls, ctrl))
					{
						throw new NotSupportedException("AddTabs {" + ctrl.GetType().FullName + "}");
					}
				}
			}
		}

		/// <summary>
		/// Create, init and add GROUP control to container
		/// </summary>
		public virtual void AddGroup(ControlCollection container, MetaFormGroup group)
		{
			if (!group.Condition.IsNullOrEmpty() && RulesService != null && !RulesService.Check((object)group.Condition, RulesInstance, RulesValues))
				return;

			var fs = new FieldSet
						{
							ID = group.Name,
							Text = group.Title,
							Width = group.Width,
							Height = group.Height,
							Visible = group.Visible,
							Enabled = group.Enabled && !group.ReadOnly
						};

			group.Control = fs;

			container.Add(fs);

			fs.AddLegend();

			var items = group.Items;

			for (int I = 0, ICnt = items.Count; I < ICnt; I++)
			{
				var ctrl = items[I];

				if (ctrl is MetaFormTable)
				{
					AddTable(fs.Controls, (MetaFormTable)ctrl);
				}
				else if (ctrl is MetaFormFieldCtrl)
				{
					var fields = new List<MetaFormCtrl>();

					I = DetectFieldGroup(items, I, fields);

					AddFieldGroup(fs.Controls, fields);
				}
				else if (ctrl is MetaFormControl)
				{
					if (((MetaFormControl)ctrl).IsField)
					{
						var fields = new List<MetaFormCtrl>();

						I = DetectFieldGroup(items, I, fields);

						AddFieldGroup(fs.Controls, fields);
					}
					else
					{
						AddControl(fs.Controls, (MetaFormControl)ctrl);
					}
				}
				else if (ctrl is MetaFormLabelCtrl)
				{
					AddLabel(fs.Controls, (MetaFormLabelCtrl)ctrl);
				}
				else if (!FireAddCtrlEv(fs.Controls, ctrl))
				{
					throw new NotSupportedException("AddGroup {" + ctrl.GetType().FullName + "}");
				}
			}
		}

		/// <summary>
		/// Create, init and add TABLE control to container
		/// </summary>
		public virtual void AddTable(ControlCollection container, MetaFormTable table)
		{
			var tbl = new System.Web.UI.WebControls.Table
						{
							ID = table.Name,
							CssClass = Const.Forms.WebForms.TableClass,
							CellPadding = 0,
							CellSpacing = 0,
							Width = table.Width,
							Height = table.Height,
							Visible = table.Visible,
							Enabled = table.Enabled && !table.ReadOnly
						};

			if (table.ShowBorders)
			{
				tbl.Attributes["border"] = "1";
				//tbl.Style[HtmlTextWriterStyle.BorderCollapse] = "collapse";
			}
			else
			{
				tbl.BorderWidth = Unit.Pixel(0);
			}

			table.Control = tbl;

			container.Add(tbl);

			foreach (var row in table.Items)
			{
				AddTableRow(tbl, row);
			}
		}

		/// <summary>
		/// Create, init and add TABLE ROW control to container
		/// </summary>
		public virtual void AddTableRow(System.Web.UI.WebControls.Table tbl, MetaFormTableRow row)
		{
			var trow = new System.Web.UI.WebControls.TableRow
						{
							ID = row.Name,
							CssClass = Const.Forms.WebForms.TableRowClass,
							ToolTip = row.Title,
							Height = row.Height,
							Visible = row.Visible,
							Enabled = row.Enabled && !row.ReadOnly
						};

			row.Control = trow;

			tbl.Rows.Add(trow);

			foreach (var cell in row.Items)
			{
				AddTableCell(trow, cell);
			}
		}

		/// <summary>
		/// Create, init and add TABLE CELL control to container
		/// </summary>
		public virtual void AddTableCell(System.Web.UI.WebControls.TableRow row, MetaFormTableCell cell)
		{
			var tcell = new System.Web.UI.WebControls.TableCell
							{
								ID = cell.Name,
								ToolTip = cell.Title,
								CssClass = Const.Forms.WebForms.TableCellClass,
								Width = cell.Width,
								Height = cell.Height,
								Visible = cell.Visible,
								Enabled = cell.Enabled && !cell.ReadOnly
							};

			cell.Control = tcell;

			row.Cells.Add(tcell);

			var items = cell.Items;

			for (int I = 0, ICnt = items.Count; I < ICnt; I++)
			{
				var ctrl = items[I];

				if (ctrl is MetaFormFieldCtrl)
				{
					var fields = new List<MetaFormCtrl>();

					I = DetectFieldGroup(items, I, fields);

					AddFieldGroup(tcell.Controls, fields);
				}
				else if (ctrl is MetaFormControl)
				{
					if (((MetaFormControl)ctrl).IsField)
					{
						var fields = new List<MetaFormCtrl>();

						I = DetectFieldGroup(items, I, fields);

						AddFieldGroup(tcell.Controls, fields);
					}
					else
					{
						AddControl(tcell.Controls, (MetaFormControl)ctrl);
					}
				}
				else if (ctrl is MetaFormLabelCtrl)
				{
					AddLabel(tcell.Controls, (MetaFormLabelCtrl)ctrl);
				}
				else if (ctrl is MetaFormGroup)
				{
					AddGroup(tcell.Controls, (MetaFormGroup)ctrl);
				}
				else if (ctrl is MetaFormTable)
				{
					AddTable(tcell.Controls, (MetaFormTable)ctrl);
				}
				else if (!FireAddCtrlEv(tcell.Controls, ctrl))
				{
					throw new NotSupportedException("AddTableCell {" + ctrl.GetType().FullName + "}");
				}
			}
		}

		/// <summary>
		/// Detect group of fields, return index at end of group
		/// </summary>
		public virtual int DetectFieldGroup(IList<MetaFormCtrl> ctrls, int startIdx, IList<MetaFormCtrl> fields)
		{
			var cidx = startIdx;

			while (cidx < ctrls.Count)
			{
				var ctrl = ctrls[cidx];

				var ffctrl = ctrl as MetaFormFieldCtrl;
				var fcctrl = ffctrl == null ? ctrl as MetaFormControl : null;

				if ((ffctrl == null) && (fcctrl == null || !fcctrl.IsField))
				{
					cidx--;
					break;
				}

				fields.Add(ctrl);

				cidx++;
			}

			return cidx;
		}

		/// <summary>
		/// Create, init and add LABEL control to container
		/// </summary>
		public virtual void AddLabel(ControlCollection container, MetaFormLabelCtrl label)
		{
			var lbl = new System.Web.UI.WebControls.Label
						{
							ID = label.Name,
							ToolTip = label.Title,
							Text = label.Title,
							Visible = label.Visible
						};

			label.Control = lbl;

			container.Add(lbl);
		}

		/// <summary>
		/// Return register directive
		/// </summary>
		public virtual string GetRegisterDirective()
		{
			return
				Const.Forms.WebForms.FormsRegisterConfigKey.GetConfigValue(Const.Forms.WebForms.FormsRegisterDefault);
		}

		/// <summary>
		/// Create, init and add CONTROL control to container
		/// </summary>
		public virtual void AddControl(ControlCollection container, MetaFormControl control)
		{
			if (!control.WebControl.IsNullOrEmpty())
			{
				var ctrlsrc = string.Concat(GetRegisterDirective(), Environment.NewLine, control.WebControl);

				var ctrl = Page.ParseControl(ctrlsrc);

				if (ctrl != null)
				{
					control.Control = ctrl;

					container.Add(ctrl);
				}
			}
			else if (!control.UserControl.IsNullOrEmpty())
			{
				var ctrl = Page.LoadControl(control.UserControl);

				if (ctrl != null)
				{
					control.Control = ctrl;

					container.Add(ctrl);
				}
			}
		}

		/// <summary>
		/// Create, init and add FIELD GROUP control to container
		/// </summary>
		public virtual void AddFieldGroup(ControlCollection container, ICollection<MetaFormCtrl> fields)
		{
			container.Add(new LiteralControl(Const.Forms.WebForms.FieldGroupHead));

			foreach (var field in fields)
			{
				if (field is MetaFormFieldCtrl)
				{
					container.Add(new LiteralControl(Const.Forms.WebForms.FieldGroupItemHead));

					AddField(container, (MetaFormFieldCtrl)field);

					container.Add(new LiteralControl(Const.Forms.WebForms.FieldGroupItemTail));
				}
				else if (field is MetaFormControl)
				{
					AddControl(container, (MetaFormControl)field);
				}
			}

			container.Add(new LiteralControl(Const.Forms.WebForms.FieldGroupTail));
		}

		/// <summary>
		/// Create, init and add FIELD control to container
		/// </summary>
		public virtual void AddField(ControlCollection container, MetaFormFieldCtrl field)
		{
			if (!field.Condition.IsNullOrEmpty() && RulesService != null && !RulesService.Check((object)field.Condition, RulesInstance, RulesValues))
				return;

			var cf = new CompositeField
						{
							ID = "cf" + field.Name,
							FieldName = field.Field.Name,
							ListId = Meta.ID,
							//EnableViewState = false,
							ControlMode = Mode.ToString().SToE(SPControlMode.Invalid)
						};

			if ((cf.ControlMode == SPControlMode.Edit || cf.ControlMode == SPControlMode.New) && (field.ReadOnly || !field.Enabled))
			{
				// When in read-only mode set display mode for control
				cf.ControlMode = SPControlMode.Display;
			}

			// Set custom templates
			if (cf.ControlMode == SPControlMode.Display && !field.DisplayTemplateName.IsNullOrEmpty())
				cf.TemplateName = field.DisplayTemplateName;
			else if ((cf.ControlMode == SPControlMode.Edit || cf.ControlMode == SPControlMode.New) && !field.EditTemplateName.IsNullOrEmpty())
				cf.TemplateName = field.EditTemplateName;

			field.Control = cf;

			container.Add(cf);
		}

		/// <summary>
		/// Add script to page
		/// </summary>
		public virtual void AddScripts(ControlCollection container, MetaFormScripts scripts)
		{
			foreach (var script in scripts.Items)
			{
				AddScript(container, script);
			}
		}

		/// <summary>
		/// Add script to page
		/// </summary>
		public virtual void AddScript(ControlCollection container, MetaFormScript script)
		{
			if (!CheckSecurity(script.Security) || !script.Visible)
				return;

			if (!script.Condition.IsNullOrEmpty() && RulesService != null && !RulesService.Check((object)script.Condition, RulesInstance, RulesValues))
				return;

			if (!script.Src.IsNullOrEmpty())
				Page.ClientScript.RegisterClientScriptInclude(script.Name, script.Src);
			else if (!script.Text.IsNullOrEmpty())
				Page.ClientScript.RegisterClientScriptBlock(typeof(Page), script.Name, script.Text, true);
		}

		/// <summary>
		/// Check security
		/// </summary>
		/// <returns>true, if security check succeeded</returns>
		public virtual bool CheckSecurity(string[] security)
		{
			if (security == null || security.Length == 0)
				return true;

			return
				security.Any(groupName => SPPrincipalExts.InGroup(User, groupName));
		}

		/// <summary>
		/// Create, init and add UNKNOWN control to container
		/// </summary>
		public virtual bool FireAddCtrlEv(ControlCollection container, MetaFormCtrl meta)
		{
			return
				AddCtrlEv != null && AddCtrlEv(container, meta);
		}

		/// <summary>
		/// Updates validation error messages
		/// </summary>
		public virtual void OnPagePreRender(object sender, EventArgs e)
		{
			if (!Page.IsValidationRequired())
				return;

			if (Page.SafeIsValid(false))
				return;

			for (int i = 0, iCnt = Page.Validators.Count; i < iCnt; i++)
			{
				try
				{
					var ival = Page.Validators[i];

					if (ival.IsValid || ival.ErrorMessage.IsNullOrEmpty())
						continue;

					var field = ival as BaseFieldControl;

					if (field != null)
					{
						ival.ErrorMessage = field.Field.Title + ": " + ival.ErrorMessage.TrimStart(field.Field.Title + ": ");
						continue;
					}

					var validator = ival as BaseValidator;

					if (validator != null && !validator.ControlToValidate.IsNullOrEmpty())
					{
						var ctrl = Page.FindControlRecursive(validator.ControlToValidate).FindParentControl<FieldMetadata>();

						if (ctrl != null)
						{
							ival.ErrorMessage = ctrl.Field.Title + ": " + ival.ErrorMessage.TrimStart(ctrl.Field.Title + ": ");
							continue;
						}
					}

					var fieldMetadata = (ival as System.Web.UI.Control).FindParentControl<FieldMetadata>();

					if (fieldMetadata != null)
					{
						ival.ErrorMessage = fieldMetadata.Field.Title + ": " + ival.ErrorMessage.TrimStart(fieldMetadata.Field.Title + ": ");
						continue;
					}

					var entityEditor = ival as EntityEditor;

					if (entityEditor != null)
					{
						ival.ErrorMessage = entityEditor.Title + ": " + ival.ErrorMessage.TrimStart(entityEditor.Title + ": ");
						continue;
					}
				}
				catch (Exception x)
				{
					H.LX("WebFormPresenter.OnPagePreRender", x);
				}
			}
		}
	}
}